package com.tianji.remark.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;
import static com.tianji.common.constants.RedisConstants.LIKE_RECORD_BIZ_KEY_PREFIX;
import static com.tianji.common.constants.RedisConstants.LIKE_TIMES_BIZ_KEY_PREFIX;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author LYQ
 * @since 2024-06-28
 */
@Service
@RequiredArgsConstructor
public class LikedRecordRedisServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final RabbitMqHelper rabbitMqHelper;
    private final StringRedisTemplate redisTemplate;

    /**
     * 点赞或取消点赞
     *
     * @param likeRecordDTO
     */
    @Override
    public void addLikedRecord(LikeRecordFormDTO likeRecordDTO) {
        //1、判断是点赞还是取消，执行操作并返回是否执行成功
        boolean isSuccess = likeRecordDTO.getLiked() ? like(likeRecordDTO) : undoLike(likeRecordDTO);
        //2、操作失败直接return
        if (!isSuccess) return;
        //3、操作成功查询点赞数量
        Long likedCount = redisTemplate.opsForSet().size(LIKE_RECORD_BIZ_KEY_PREFIX + likeRecordDTO.getBizId());
        //4、将点赞次数数据存入redis，redis定期持久化到mysql
        redisTemplate.opsForZSet().add(LIKE_TIMES_BIZ_KEY_PREFIX + likeRecordDTO.getBizType(), likeRecordDTO.getBizId().toString(), likedCount);
    }

    /**
     * 新增点赞记录
     *
     * @param likeRecordDTO
     * @return
     */
    private boolean like(LikeRecordFormDTO likeRecordDTO) {
        //将该点赞记录数据存入redis，如果已经存在，或者执行存入失败返回false
        //1、组装Key
        String key = LIKE_RECORD_BIZ_KEY_PREFIX + likeRecordDTO.getBizId();
        //2、执行添加指令
        Long result = redisTemplate.opsForSet().add(key, UserContext.getUser().toString());
        //3、根据执行结果判断是否执行成功,set集合，如果对应key的元素值存在，执行结果为0
        return result != null && result > 0;
    }

    /**
     * 删除点赞记录
     *
     * @param likeRecordDTO
     * @return
     */
    private boolean undoLike(LikeRecordFormDTO likeRecordDTO) {
        //1、直接删除对应redis中的数据
        String key = LIKE_RECORD_BIZ_KEY_PREFIX + likeRecordDTO.getBizId();
        Long result = redisTemplate.opsForSet().remove(key, UserContext.getUser().toString());
        //2、根据执行结果判断是否执行成功
        return result != null && result > 0;
    }

    /**
     * 根据业务ID集合查询是否已经点赞
     *
     * @param bizIds
     */
    @Override
    public Set<Long> isLiked(List<Long> bizIds) {
        //1、根据业务ID集合查询点赞记录
        //查询redis中set集合内，当前用户id是否属于对应业务ID集合
        List<Object> resultList = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection connet = (StringRedisConnection) connection;
            //循环遍历，批量包装请求
            for (Long bizId : bizIds) {
                connet.sIsMember(LIKE_RECORD_BIZ_KEY_PREFIX + bizId, UserContext.getUser().toString());
            }
            return null;
        });
//        //2、根据redis批量处理结果集判断对应索引位置的id是否点赞
//        Set<Long> likedList = new HashSet<>(bizIds.size());
//        for (int i = 0; i < resultList.size(); i++) {
//            if (Boolean.TRUE.equals(resultList.get(i))){
//                likedList.add(bizIds.get(i));
//            }
//        }
//        return likedList;

        //stream刘处理方式
        return IntStream.range(0, resultList.size())
                .filter(i -> Boolean.TRUE.equals(resultList.get(i)))
                .mapToObj(i -> bizIds.get(i))
                .collect(Collectors.toSet());
    }

    /**
     * 定时任务触发，查询redis 中的点赞次数，并发送消息到对应微服务持久化到mysql
     *
     * @param bizType
     */
    @Override
    public void checkLikedTimesAndSendMsg(String bizType, Integer maxBizSize) {
        //1、从redis中查询点赞次数
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(LIKE_TIMES_BIZ_KEY_PREFIX + bizType, maxBizSize);
        //2、从元组中获取对应数据
        while (ObjectUtils.isNotEmpty(typedTuples)) {
            List<LikedTimesDTO> likedTimesDTOList = new ArrayList<>(maxBizSize);
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                //循环遍历封装数据
                String bizId = typedTuple.getValue();
                Double likedTimes = typedTuple.getScore();
                likedTimesDTOList.add(LikedTimesDTO.of(Long.valueOf(bizId), likedTimes.intValue()));
            }
            //3、发送消息
            rabbitMqHelper.send(
                    LIKE_RECORD_EXCHANGE,
                    StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
                    likedTimesDTOList);
            //4、收集下一个30条点赞次数数据
            typedTuples = redisTemplate.opsForZSet().popMin(LIKE_TIMES_BIZ_KEY_PREFIX, maxBizSize);
        }
    }
}
